package com.cskaoyan.mall.product.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.cskaoyan.mall.common.constant.RedisConst;
import com.cskaoyan.mall.mq.constant.MqResultEnum;
import com.cskaoyan.mall.mq.constant.MqTopicConst;
import com.cskaoyan.mall.mq.producer.BaseProducer;
import com.cskaoyan.mall.product.cache.RedisCache;
import com.cskaoyan.mall.product.client.SearchApiClient;
import com.cskaoyan.mall.product.converter.dto.PlatformAttributeInfoConverter;
import com.cskaoyan.mall.product.converter.dto.SkuInfoConverter;
import com.cskaoyan.mall.product.converter.dto.SpuInfoConverter;
import com.cskaoyan.mall.product.converter.param.SkuInfoParamConverter;
import com.cskaoyan.mall.product.dto.PlatformAttributeInfoDTO;
import com.cskaoyan.mall.product.dto.SkuInfoDTO;
import com.cskaoyan.mall.product.dto.SkuInfoPageDTO;
import com.cskaoyan.mall.product.dto.SpuSaleAttributeInfoDTO;
import com.cskaoyan.mall.product.mapper.*;
import com.cskaoyan.mall.product.model.*;
import com.cskaoyan.mall.product.query.SkuInfoParam;
import com.cskaoyan.mall.product.service.SkuService;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @Author Pudding
 * @Date 2024/7/1 22:18
 */
@Service
public class SkuServiceImpl implements SkuService {

    @Autowired
    SkuInfoMapper skuInfoMapper;
    @Autowired
    SkuInfoConverter skuInfoConverter;
    @Autowired
    SkuInfoParamConverter skuInfoParamConverter;
    @Autowired
    SkuImageMapper skuImageMapper;
    @Autowired
    SkuPlatformAttrValueMapper skuPlatformAttrValueMapper;
    @Autowired
    SkuSaleAttrValueMapper skuSaleAttrValueMapper;
    @Autowired
    SpuImageMapper spuImageMapper;
    @Autowired
    SpuSaleAttrInfoMapper spuSaleAttrInfoMapper;
    @Autowired
    SpuInfoConverter spuInfoConverter;
    @Autowired
    PlatformAttrInfoMapper platformAttrInfoMapper;
    @Autowired
    PlatformAttrValueMapper platformAttrValueMapper;
    @Autowired
    PlatformAttributeInfoConverter platformAttributeInfoConverter;
    @Autowired
    RedissonClient redissonClient;
    @Autowired
    SearchApiClient searchApiClient;
    @Autowired
    BaseProducer baseProducer;

    //1. SKU数据的保存
    @Override
    public void saveSkuInfo(SkuInfoParam skuInfoParam) {

        //1.1 将参数对象对象转换为PO对象
        SkuInfo skuInfo = skuInfoParamConverter.SkuInfoParam2Info(skuInfoParam);
        //1.2 保存sku基本信息保存到sku_info表
        skuInfoMapper.insert(skuInfo);
        //1.3 根据skuInfo封装的信息获取sku图片列表
        List<SkuImage> skuImageList = skuInfo.getSkuImageList();
        if (!skuImageList.isEmpty()) {
            for (SkuImage skuImage : skuImageList) {
                skuImage.setSkuId(skuInfo.getId());
                skuImageMapper.insert(skuImage);
            }
        }
        //1.4 根据skuInfo封装的信息获取sku平台属性值信息
        List<SkuPlatformAttributeValue> skuPlatformAttributeValueList = skuInfo.getSkuPlatformAttributeValueList();
        if (!skuPlatformAttributeValueList.isEmpty()) {
            for (SkuPlatformAttributeValue skuPlatformAttributeValue : skuPlatformAttributeValueList) {
                skuPlatformAttributeValue.setSkuId(skuInfo.getId());
                skuPlatformAttrValueMapper.insert(skuPlatformAttributeValue);
            }
        }

        //1.5 根据skuInfo封装的信息获取sku销售属性值信息
        List<SkuSaleAttributeValue> skuSaleAttributeValueList = skuInfo.getSkuSaleAttributeValueList();
        if (!skuSaleAttributeValueList.isEmpty()) {
            for (SkuSaleAttributeValue skuSaleAttributeValue : skuSaleAttributeValueList) {
                skuSaleAttributeValue.setSkuId(skuInfo.getId());
                skuSaleAttributeValue.setSpuId(skuInfo.getSpuId());
                skuSaleAttrValueMapper.insert(skuSaleAttributeValue);
            }
        }

    }

    //2. 根据分页参数查询SKU分页数据
    @Override
    public SkuInfoPageDTO getPage(Page<SkuInfo> pageParam) {
        //2.1 根据分页参数对象进行查询分页结果
        Page<SkuInfo> skuInfoPage = skuInfoMapper.selectPage(pageParam, null);
        //2.2 根据分页结果获取查询这一页的信息和总页数
        List<SkuInfo> records = skuInfoPage.getRecords();
        List<SkuInfoDTO> skuInfoDTOS = skuInfoConverter.skuInfoPOs2DTOs(records);
        Integer total = (int) skuInfoPage.getTotal();

        //2.3 将上述数据封装到对象中
        SkuInfoPageDTO skuInfoPageDTO = new SkuInfoPageDTO(skuInfoDTOS, total);

        return skuInfoPageDTO;
    }

    //3. SKU商品上架
    @Override
    public void onSale(Long skuId) {

        //3.1 构造修改条件，修改对应商品的is_sale = 1
        LambdaUpdateWrapper<SkuInfo> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        lambdaUpdateWrapper.eq(SkuInfo::getId, skuId)
                .set(SkuInfo::getIsSale, 1);
        skuInfoMapper.update(null, lambdaUpdateWrapper);

        //3.2 根据指定名称key，获取布隆过滤器对象
        RBloomFilter<Object> bloomFilter = redissonClient.getBloomFilter(RedisConst.SKU_BLOOM_FILTER);

        //3.3 把元素添加到布隆过滤器
        bloomFilter.add(skuId);

        //方式一：远程调用搜索服务对外暴露的上架接口
        //searchApiClient.upperGoods(skuId);
        //方式二：改造，通过RocketMQ，给搜索服务发送普通消息，搜索服务接受消息，消费消息，完成上架
        baseProducer.sendMessage(MqTopicConst.PRODUCT_ONSALE_TOPIC, skuId);

    }
    //3. 查询出所有已经上架的商品，把他们放入布隆过滤器中
    public void initBloomFilter() {

        //3.1 构造查询条件
        LambdaQueryWrapper<SkuInfo> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(SkuInfo::getIsSale, 1)
                          .eq(SkuInfo::getIsDeleted, 0);

        //3.2 根据查询条件查询出所有已经上架的商品
        List<SkuInfo> skuInfoList = skuInfoMapper.selectList(lambdaQueryWrapper);

        //3.3 根据指定名称key，获取布隆过滤器对象
        RBloomFilter<Object> bloomFilter = redissonClient.getBloomFilter(RedisConst.SKU_BLOOM_FILTER);

        //3.4 把元素添加到布隆过滤器
        for (SkuInfo skuInfo : skuInfoList) {
            bloomFilter.add(skuInfo.getId());
        }
    }

    //4. SKU商品下架
    @Override
    public void offSale(Long skuId) {

        //4.1 构造修改条件
        LambdaUpdateWrapper<SkuInfo> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        lambdaUpdateWrapper.eq(SkuInfo::getId, skuId)
                .set(SkuInfo::getIsSale, 0);
        skuInfoMapper.update(null, lambdaUpdateWrapper);

        //方式一：远程调用搜索服务对外暴露的下架接口
        //searchApiClient.lowerGoods(skuId);
        //方式二：改造: 通过RocketMQ，给搜索服务发送消息，搜索服务接受消息，消费消息，完成下架
        baseProducer.sendMessage(MqTopicConst.PRODUCT_OFFSALE_TOPIC, skuId);
    }

    //5. 根据skuId查询SKU商品详情
    @Override
    @RedisCache(prefix = "skuInfo:")
    public SkuInfoDTO getSkuInfo(Long skuId) {

        //5.1 根据id查询SKU商品基本信息
        SkuInfo skuInfo = skuInfoMapper.selectById(skuId);

        //5.2 给skuInfo中List<SkuImage> SkuImageList字段赋值
        LambdaQueryWrapper<SkuImage> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(SkuImage::getSkuId, skuId);
        List<SkuImage> skuImageList = skuImageMapper.selectList(lambdaQueryWrapper);
        skuImageList.stream().forEach(skuImage -> {
            //5.2.1 查询spu图片信息表，获取imgUrl、imgName为SkuImage对象赋值
            SpuImage spuImage = spuImageMapper.selectById(skuImage.getSpuImgId());
            skuImage.setImgName(spuImage.getImgName());
            skuImage.setImgUrl(spuImage.getImgUrl());
        });
        //5.2.2 把skuImage的集合 设置到skuInfo中
        skuInfo.setSkuImageList(skuImageList);

        //5.3 给skuInfo中的List<SkuPlatformAttributeValue> skuPlatformAttributeValueList字段赋值

        //5.4 给skuInfo中的List<SkuSaleAttributeValue> skuSaleAttributeValueList;

        //5.5 将skuInfo这个PO对象转换为SkuInfoDTO对象
        SkuInfoDTO skuInfoDTO = skuInfoConverter.skuInfoPO2DTO(skuInfo);

        return skuInfoDTO;
    }

    //6. 根据skuId查询sku_Info中的价格
    @Override
    public BigDecimal getSkuPrice(Long skuId) {

        SkuInfo skuInfo = skuInfoMapper.selectById(skuId);
        BigDecimal price = BigDecimal.ZERO;
        if (skuInfo != null){
            price = skuInfo.getPrice();
        }
        return price;
    }

    //7. 根据skuId和spuId获取sku完整的销售属性值列表
    @Override
    @RedisCache(prefix = "spuSaleAttrListCheckBySku:")
    public List<SpuSaleAttributeInfoDTO> getSpuSaleAttrListCheckBySku(Long skuId, Long spuId) {

        //7.1 进行自定义xml查询
        List<SpuSaleAttributeInfo> spuSaleAttributeInfos = spuSaleAttrInfoMapper.selectSpuSaleAttrListCheckedBySku(skuId, spuId);
        //7.2 将查询PO对象转换为DTO
        List<SpuSaleAttributeInfoDTO> spuSaleAttributeInfoDTOS = spuInfoConverter.spuSaleAttributeInfoPOs2DTOs(spuSaleAttributeInfos);

        return spuSaleAttributeInfoDTOS;
    }

    //8. 根据skuId获取sku商品的平台属性集合（规格包装中，查询这个sku的平台属性和平台属性值，一个平台属性对应一个平台属性值）
    @Override
    @RedisCache(prefix = "platformAttributeInfoList:")
    public List<PlatformAttributeInfoDTO> getPlatformAttrInfoBySku(Long skuId) {

        //这里我们通过sku_paltform_attribute_value表就可以查询出该sku商品对应的平台属性:平台属性值的列表，但是这个关系表没有具体的值，只有平台属性值表和平台属性表的id
        //8.1 第一步我们先根据skuId查询出sku平台属性值关系表的list
        LambdaQueryWrapper<SkuPlatformAttributeValue> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(SkuPlatformAttributeValue::getSkuId, skuId);
        List<SkuPlatformAttributeValue> skuPlatformAttributeValues = skuPlatformAttrValueMapper.selectList(lambdaQueryWrapper);

        //8.2 通过流遍历这个list的同时根据list中的每个对象的attrId和valueId查询出对应的一个平台属性和一个平台属性值，并放入PlatformAttributeInfoDTO对象中
        List<PlatformAttributeInfo> platformAttributeInfoList = skuPlatformAttributeValues.stream().map(skuPlatformAttributeValue -> {
            //8.2.1 根据attrId查询平台属性
            PlatformAttributeInfo platformAttributeInfo = platformAttrInfoMapper.selectById(skuPlatformAttributeValue.getAttrId());
            //8.2.1 根据valueId查询平台属性值
            PlatformAttributeValue platformAttributeValue = platformAttrValueMapper.selectById(skuPlatformAttributeValue.getValueId());
            //8.2.3 把平台属性值放入平台属性对象platformAttributeInfo的attrValueList中的PlatformAttributeValue的valueName属性中
            platformAttributeInfo.setAttrValueList(Arrays.asList(platformAttributeValue)); //用于将数组转换为列表(List)。这里假设platformAttributeValue是一个数组

            return platformAttributeInfo;
        }).collect(Collectors.toList());

        //8.3 将PO读写转换为DTO对象
        List<PlatformAttributeInfoDTO> platformAttributeInfoDTOS = platformAttributeInfoConverter.platformAttributeInfoPOs2DTOs(platformAttributeInfoList);

        return platformAttributeInfoDTOS;
    }
}
